/*
	jGrouse framework
	Copyright 2007 by Denis Riabtchik 
	All rights reserved.
	
	Licensed under modified BSD license. See license.txt for details
*/


/**
	@module jgrouse.widgets.Container
	@requires jgrouse.widgets.TemplateParser
	@requires jgrouse.util.MvcDriver
	@requires jgrouse.util.Sets
	@requires jgrouse.widgets.Widget
*/
jgrouse.module(
{
	name : 'jgrouse.widgets.Container',
	requires : ['jgrouse.widgets.TemplateParser', 'jgrouse.util.MvcDriver', 'jgrouse.util.Sets', 'jgrouse.widgets.Widget'],
	imports : ['jgrouse.widgets.TemplateParser', 'jgrouse.util.MvcDriver', 'jgrouse.util.PrimitiveSet', 'jgrouse.string.*'],
	
	body : function(embed)
	{
		eval(embed);
		
		
		
		
		jgrouse.widgets.registerWidget('container', 'jgrouse.widgets.Container');
		
		/**
			Container is a compound widget that consists of other widgets (thus creating View) 
			and has own Model and Controller.
			@class jgrouse.widgets.Container
			@extends jgrouse.widgets.Widget
		*/
		jgrouse.define('jgrouse.widgets.Container', 'jgrouse.widgets.Widget', function(salt)
		{
			eval(salt);
			
			return {
				/**
					@constructor initialize
					@inheritdesc
				*/
				initialize : function(options, styles)
				{
					_super(this, 'initialize', options, styles);
					/**
						@variable {jgrouse.widgets.Layout} _layout
					*/
					this._layout = null;
					
					/**
					 * MVC driver from parent container
					 * @variable {jgrouse.util.MVCDriver} _parentMvcDriver
					 */
					
					this._parentMvcDriver = null;
					
					/**
						model for the controller
						@variable {Object} _data 
					*/
					this._data = null;
					/**
						set of exported (i.e. the ones exposed to the outside world)property names
						@variable {jgrouse.util.Set} ?  
					*/
					this._exportedProps = new PrimitiveSet();
					
					
					/**
					 * Bindings to parent container
					 * @variable {String} _externBindings
					 */
					this._externBindings = null;
					
					/**
						Controller's class. 
						@variable _controllerClass
					*/
					this._controllerClass = null;
					/**
						Name of function that acts like a factory for controllers
						@variable _controllerFactory 
					*/
					this._controllerFactory = null;
				},
				
				/**
					Create a controller for the container
					@function {Object} createController
				*/
				createController : function()
				{
					if (this._controllerClass != null)
					{
						var cf = jgrouse.resolveName(this._controllerClass);
						if (cf === null)
						{
							throw "Cannot find controller's constructor " + this._controllerClass;
						}
						return new cf();
					}
					else if (this._controllerFactory != null)
					{
						var cf = jgrouse.resolveName(this._controllerFactory);
						if (cf === null)
						{
							throw "Cannot find controller's factory " + this._controllerFactory;
						}
						return cf();
					}
					return null;		
				},
				
				/**
					Set controller's class. Would initiate creation of controller and MVC driver
					@function setControllerClass
					@param {String} controllerClassName
				*/
				setControllerClass : function(controllerClassName)
				{
					this._controllerClass = controllerClassName;
					//this._initController();
				},


				/**
					Set controller's factory function. Would initiate creation of controller and MVC driver
					@function setControllerFactory
					@param {String} controllerFactoryName
				*/
				setControllerFactory : function(controllerFactoryName)
				{
					this._controllerFactory = controllerFactoryName;
				},
				
				/**
					Generic handler for property changes.
					If the changed property was exported, then all listeners subscribed to that 
					property are notified.
					@function onPropertyChanged
					@param {jgrouse.util.PropertyChangeEvent} event
				*/
				onPropertyChanged : function(event)
				{
					var propName = event.getPropertyName();
					
					if (this._exportedProps.exists(propName))
					{
						var newEvent = new jgrouse.util.PropertyChangeEvent(this, propName, event.getNewValue(), event.getOldValue());
						this._parentMvcDriver.onPropertyChanged(newEvent);
					}
				},
				
				/**
					Set data for the container's model
					@function setData
					@param {Object} data - model's data
				*/
				setData : function(data)
				{
					this._data = data;
					this._mvcDriver.setData(data);
				},
				
				/**
					Set the name of file with HTML template for controller
					@function setTemplateName
					@param {String} templateName 
				*/
				setTemplateName : function(templateName)
				{
					var template = thisModule.getResource(templateName);
					this.setTemplate(template);
				},
				
				
				/**
					Create layout from template
					@function {Layout} createLayout
					@paramset Set template string
					@param {String} template - string with HTML template
					@paramset Create a subtemplate from given template
					@param {TemplateParser} template - parent template parser
					@returns created layout. 
				*/
				createLayout : function(template)
				{
					if (!(template instanceof jgrouse.widgets.TemplateParser))
					{
						return new TemplateParser(template);
					}
					else
					{
						return template.subTemplate();
					}
				},
				
				/**
					Set template for container. 
					@function setTemplate
					@paramset Set template string
					@param {String} template - string with HTML text of the template
					@paramset Set template parser
					@param {TemplateParser} template - parent template
				*/
				setTemplate : function(template)
				{
					var layout = this.createLayout(template);
					this.setLayout(layout);
				},

				/**
					Set layout for container
					@function setLayout
					@param {jgrouse.layout.Layout} layout - layout to set
				*/				
				setLayout : function(layout)
				{
					this._layout = layout;
					this._layout.doParse();
					var athis = this;
					this._layout.forEach(function(widget)
					{	
						athis._internalAddWidget(widget);
					});
					
					
					var members = this._layout.getMembers();
					if (members)
					{
						members.forEach(function(member, memberName)
						{
							var setter = 'set' + jgrouse.string.upperFirst(memberName);
							if (athis[setter])
							{
								athis[setter](member);
							}
							else
							{
								athis[memberName] = member;
							}
							
						});
					}
				},
				
				/**
					Bind container to mvcDriver. If container does not have its own controller, 
					it would bind all contained widgets to given mvcDriver
					@function bind
					@param {jgrouse.util.MvcDriver} mvcDriver - mvcDriver to be bound
					@see jgrouse.widgets.Widget.bind
				*/
				bind : function(mvcDriver)
				{
					var controller = this.createController();
					this._parentMvcDriver = mvcDriver;
					if (controller)
					{
						var mvc = new MvcDriver(controller);
						_super(this, 'bind', mvc);
						this.setData({});
						this._bindParentMvc();
					}
					else
					{
						if (this._externBindings)
						{
							throw "Cannot use externBindings when container does not have own controller";
						}
						_sup(this, arguments);
					}
					this._bindChildren();
					
				},
				
				/**
				 * @function unbind
				 * @inheritdesc
				 */
				
				unbind : function()
				{
					var hasOwn = this._parentMvcDriver && (this._parentMvcDriver === this._mvcDriver);
					_sup(this, arguments);
					if (hasOwn)
					{
						this._parentMvcDriver.unbind(this);
					}
				},
				
				_bindParentMvc : function()
				{
					if (this._externBindings)
					{
						var pairs = this._externBindings.split(';');
						for (var i = 0; i < pairs.length; i++)
						{
							var pair = pairs[i].split(':');
							var viewProp = trim(pair[0]);
							var modelProp = trim(pair[1]);
							if (!viewProp)
							{
								viewProp = 'value';
							}
							this._exportedProps.put(viewProp);
							this._parentMvcDriver.bind(this, viewProp, modelProp);				
						}
					}
				},
				
				_bindChildren : function()
				{
					var athis = this;
					if (this._layout)
					{
						this._layout.forEach(function(item)
						{
							item.bind(athis._mvcDriver);
						});
					}
				},
				
				/**
					@function add
					@inheritdesc
				*/
				add : function(parentDom, refPoint)
				{
					this._layout.render(this.getDomElement());
					_sup(this, arguments);
				},
				
				/**
					@function remove
					@inheritdesc
				*/
				remove : function(doDispose)
				{
					if (this._layout && this._domElement)
					{
						this._layout.remove(this._domElement, doDispose);
					}
					_sup(this, arguments);
				},
				
				/**
					@function dispose
					@inheritdesc
				*/
				dispose : function()
				{
					if (this._layout)
					{
						this._layout.dispose();
					}
					_super(this, 'dispose');
				},
				
				_internalAddWidget : function(widget)
				{
					widget.setOwner(this);
					if (this._mvcDriver)
					{
						widget.bind(this._mvcDriver);
					}
					this.resetBounds();
				},
				
				/**
					Add widget to container. Widget would be added to layout, bound to mvcDriver (if applicable) and 
					rendered if container was rendered.
					@function addWidget 
					@param {jgrouse.widget.Widget} widget - widget to add
					@param {Object} constraint - indicator where the widget should be added. Actual type of constraint 
					depends on the type of layout
				*/
				addWidget : function(widget, constraints)
				{
					this._internalAddWidget(widget);
					this._layout.addWidget(widget, constraints);
				},
				
				/**
					Remove widget from container and optionally dispose it. 
					@function {Object} removeWidget
					@param {jgrouse.widget.Widget} widget - widget to remove
					@param {Object} constraint - constraint that was used by widget
					@returns constraint that was used by widget
				*/
				removeWidget : function(widget, doDispose)
				{
					widget.setOwner(null);
					widget.unbind();
					var constraint = this._layout.removeWidget(widget, doDispose);
					this.resetBounds();
					return constraint;
				},
				
				/**
					@function setShowing
					@inheritdesc
				*/
				setShowing : function(isShowing)
				{
					_sup(this, arguments);
					this._layout.forEach(function(widget)
					{
						widget.resetShowing();
					});
					this.resetBounds();
				},
				
				/**
				 * Set bindings to parent container's MVC driver. Bindings are set in the same format
				 * as bindings to own MVC driver
				 * @function setExternBindings
				 * @param {String} bindings
				 */
				setExternBindings : function(bindings)
				{
					this._externBindings = bindings;
				},
				
				/**
				 * Trigger layout of container's content
				 * @function layout
				 */
				layout : function()
				{
					this._layout.layout();
				}
				
				
			}
		});
	}
});